﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace Core.Security
{
    public enum HashAlgorithmEnum
    {
        SHA1,
        SHA256,
        SHA384,
        SHA512,
        MD5
    }

    public enum EncryptionAlgorithmEnum
    {
        Rijndael,
        TripleDes
    }
    public class Security
    {
        /// <summary>
        /// Generates a hash for the given plain text value and returns a
        /// base64-encoded result. Before the hash is computed, a random salt
        /// is generated and appended to the plain text. This salt is stored at
        /// the end of the hash value, so it can be used later for hash
        /// verification.
        /// </summary>
        /// <returns>
        /// Hash value formatted as a base64-encoded string.
        /// </returns>
        public static string ComputeHash(string plainText,
                                         HashAlgorithmEnum hashAlgorithm,
                                         byte[] saltBytes)
        {
            if (saltBytes == null)
            {
                int minSaltSize = 4;
                int maxSaltSize = 8;

                Random random = new Random();
                int saltSize = random.Next(minSaltSize, maxSaltSize);

                saltBytes = new byte[saltSize];
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                rng.GetNonZeroBytes(saltBytes);
            }

            // Convert plain text into a byte array.
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

            byte[] plainTextWithSaltBytes =
                    new byte[plainTextBytes.Length + saltBytes.Length];

            for (int i = 0; i < plainTextBytes.Length; i++)
                plainTextWithSaltBytes[i] = plainTextBytes[i];

            // Append salt bytes to the resulting array.
            for (int i = 0; i < saltBytes.Length; i++)
                plainTextWithSaltBytes[plainTextBytes.Length + i] = saltBytes[i];

            HashAlgorithm hash;

            if (hashAlgorithm == null)
                hashAlgorithm = HashAlgorithmEnum.MD5;

            switch (hashAlgorithm)
            {
                case HashAlgorithmEnum.SHA1:
                    hash = new SHA1Managed();
                    break;

                case HashAlgorithmEnum.SHA256:
                    hash = new SHA256Managed();
                    break;

                case HashAlgorithmEnum.SHA384:
                    hash = new SHA384Managed();
                    break;

                case HashAlgorithmEnum.SHA512:
                    hash = new SHA512Managed();
                    break;

                case HashAlgorithmEnum.MD5:
                    hash = new MD5CryptoServiceProvider();
                    break;

                default:
                    hash = new MD5CryptoServiceProvider();
                    break;
            }

            // Compute hash value of our plain text with appended salt.
            byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);

            // Create array which will hold hash and original salt bytes.
            byte[] hashWithSaltBytes = new byte[hashBytes.Length +
                                                saltBytes.Length];

            for (int i = 0; i < hashBytes.Length; i++)
                hashWithSaltBytes[i] = hashBytes[i];
            for (int i = 0; i < saltBytes.Length; i++)
                hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];

            // Convert result into a base64-encoded string.
            string hashValue = Convert.ToBase64String(hashWithSaltBytes);

            return hashValue;
        }

        /// <summary>
        /// Compares a hash of the specified plain text value to a given hash
        /// value. Plain text is hashed with the same salt value as the original
        /// hash.
        /// </summary>
        /// <returns>
        /// If computed hash mathes the specified hash the function the return
        /// value is true; otherwise, the function returns false.
        /// </returns>
        public static bool VerifyHash(string plainText,
                                      HashAlgorithmEnum hashAlgorithm,
                                      string hashValue)
        {
            // Convert base64-encoded hash value into a byte array.
            byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);

            int hashSizeInBits, hashSizeInBytes;

            if (hashAlgorithm == null)
                hashAlgorithm = HashAlgorithmEnum.MD5;

            // Size of hash is based on the specified algorithm.
            switch (hashAlgorithm)
            {
                case HashAlgorithmEnum.SHA1:
                    hashSizeInBits = 160;
                    break;

                case HashAlgorithmEnum.SHA256:
                    hashSizeInBits = 256;
                    break;

                case HashAlgorithmEnum.SHA384:
                    hashSizeInBits = 384;
                    break;

                case HashAlgorithmEnum.SHA512:
                    hashSizeInBits = 512;
                    break;

                case HashAlgorithmEnum.MD5:
                    hashSizeInBits = 128;
                    break;

                default:
                    hashSizeInBits = 128;
                    break;
            }

            hashSizeInBytes = hashSizeInBits / 8;

            if (hashWithSaltBytes.Length < hashSizeInBytes)
            {
                return false;
            }
            byte[] saltBytes = new byte[hashWithSaltBytes.Length -
                                        hashSizeInBytes];
            for (int i = 0; i < saltBytes.Length; i++)
                saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];

            // Compute a new hash string.
            string expectedHashString =
                        ComputeHash(plainText, hashAlgorithm, saltBytes);

            // If the computed hash matches the specified hash,
            // the plain text value must be correct.
            return (hashValue == expectedHashString);
        }

        public static bool verifyMd5Hash(string input, byte[] salt, string hash)
        {
            // Hash the input.

            string hashOfInput = Security.ComputeHash(input, HashAlgorithmEnum.MD5, salt);
            // Create a StringComparer an compare the hashes.
            StringComparer comparer = StringComparer.OrdinalIgnoreCase;

            if (0 == comparer.Compare(hashOfInput, hash))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Encrypts specified plaintext using Rijndael symmetric key algorithm
        /// and returns a base64-encoded result.
        /// </summary>
        /// <example>
        /// string   plainText = "Hello, World!";    // original plaintext
        ///string passPhrase = "Pas5pr@se";        // can be any string
        ///string saltValue = "s@1tValue";        // can be any string
        ///string hashAlgorithm = "SHA1";             // can be "MD5"
        ///int passwordIterations = 2;                  // can be any number
        ///string initVector = "@1B2c3D4e5F6g7H8"; // must be 16 bytes
        ///int keySize = 256;                // can be 192 or 128
        /// </example>
        /// <returns>
        /// Encrypted value formatted as a base64-encoded string.
        /// </returns>
        public static string Encrypt(string plainText,
                                     string passPhrase,
                                     string saltValue,
                                     HashAlgorithmEnum hashAlgorithm,
                                     int passwordIterations,
                                     string initVector,
                                     int keySize)
        {
            if (passPhrase == null)
            {
                passPhrase = "Pas5pr@se";
            }
            if (saltValue == null)
            {
                saltValue = "s@1tValue";
            }
            if (passwordIterations == 0)
            {
                passwordIterations = 2;
            }
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            hashAlgorithm.ToString(),
                                                            passwordIterations);

            byte[] keyBytes = password.GetBytes(keySize / 8);

            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                             keyBytes,
                                                             initVectorBytes);
            MemoryStream memoryStream = new MemoryStream();
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                         encryptor,
                                                         CryptoStreamMode.Write);
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            byte[] cipherTextBytes = memoryStream.ToArray();
            memoryStream.Close();
            cryptoStream.Close();
            string cipherText = Convert.ToBase64String(cipherTextBytes);
            return cipherText;
        }

        /// <summary>
        /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
        /// </summary>
        /// <returns>
        /// Decrypted string value.
        /// </returns>
        /// <remarks>
        /// Most of the logic in this function is similar to the Encrypt
        /// logic. In order for decryption to work, all parameters of this function
        /// - except cipherText value - must match the corresponding parameters of
        /// the Encrypt function which was called to generate the
        /// ciphertext.
        /// </remarks>
        /// <example>
        /// string   plainText = "Hello, World!";    // original plaintext
        ///string passPhrase = "Pas5pr@se";        // can be any string
        ///string saltValue = "s@1tValue";        // can be any string
        ///string hashAlgorithm = "SHA1";             // can be "MD5"
        ///int passwordIterations = 2;                  // can be any number
        ///string initVector = "@1B2c3D4e5F6g7H8"; // must be 16 bytes
        ///int keySize = 256;                // can be 192 or 128
        /// </example>
        public static string Decrypt(string cipherText,
                                     string passPhrase,
                                     string saltValue,
                                     HashAlgorithmEnum hashAlgorithm,
                                     int passwordIterations,
                                     string initVector,
                                     int keySize)
        {
            if (passPhrase == null)
            {
                passPhrase = "Pas5pr@se";
            }
            if (saltValue == null)
            {
                saltValue = "s@1tValue";
            }
            if (passwordIterations == 0)
            {
                passwordIterations = 2;
            }
            byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
            byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

            // Convert our ciphertext into a byte array.
            byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
            PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                            passPhrase,
                                                            saltValueBytes,
                                                            hashAlgorithm.ToString(),
                                                            passwordIterations);
            byte[] keyBytes = password.GetBytes(keySize / 8);
            RijndaelManaged symmetricKey = new RijndaelManaged();
            symmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                                                             keyBytes,
                                                             initVectorBytes);

            MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
            CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                          decryptor,
                                                          CryptoStreamMode.Read);
            byte[] plainTextBytes = new byte[cipherTextBytes.Length];
            int decryptedByteCount = cryptoStream.Read(plainTextBytes,
                                                       0,
                                                       plainTextBytes.Length);

            memoryStream.Close();
            cryptoStream.Close();
            string plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                       0,
                                                       decryptedByteCount);
            return plainText;
        }

        public static string TripleDESEnCrypt(string value, string key)
        {

            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();

            des.IV = new byte[8];

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(key, new byte[-1 + 1]);

            des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

            MemoryStream ms = new MemoryStream((value.Length * 2) - 1);

            CryptoStream encStream = new CryptoStream(ms, des.CreateEncryptor(), CryptoStreamMode.Write);

            byte[] plainBytes = Encoding.UTF8.GetBytes(value);

            encStream.Write(plainBytes, 0, plainBytes.Length);

            encStream.FlushFinalBlock();

            byte[] encryptedBytes = new byte[(int)ms.Length - 1 + 1];

            ms.Position = 0;

            ms.Read(encryptedBytes, 0, (int)ms.Length);

            encStream.Close();


            return Convert.ToBase64String(encryptedBytes);
        }



        public static string TripleDESDeCrypt(string value, string key)
        {

            TripleDESCryptoServiceProvider des = new TripleDESCryptoServiceProvider();

            des.IV = new byte[8];

            PasswordDeriveBytes pdb = new PasswordDeriveBytes(key, new byte[-1 + 1]);

            des.Key = pdb.CryptDeriveKey("RC2", "MD5", 128, new byte[8]);

            byte[] encryptedBytes = Convert.FromBase64String(value);

            MemoryStream ms = new MemoryStream(value.Length);

            CryptoStream decStream = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);

            decStream.Write(encryptedBytes, 0, encryptedBytes.Length);

            decStream.FlushFinalBlock();

            byte[] plainBytes = new byte[(int)ms.Length - 1 + 1];

            ms.Position = 0;

            ms.Read(plainBytes, 0, (int)ms.Length);

            decStream.Close();


            return Encoding.UTF8.GetString(plainBytes);
        }

        /// <summary>
        /// This Class using for encrypt member id when you want to working with old social network
        /// </summary>
        public static class _Security
        {
            public static string Encrypt(string myString)
            {
                string Key = "8b,g[viNiuj";
                TripleDESCryptoServiceProvider CryptDES3 = new TripleDESCryptoServiceProvider();
                MD5CryptoServiceProvider CryptMD5Hash = new MD5CryptoServiceProvider();
                CryptDES3.Key = CryptMD5Hash.ComputeHash(ASCIIEncoding.ASCII.GetBytes(Key));
                CryptDES3.Mode = CipherMode.ECB;
                ICryptoTransform Desdencrypt = CryptDES3.CreateEncryptor();
                var MyASCIIEncoding = new ASCIIEncoding();
                byte[] buff = ASCIIEncoding.ASCII.GetBytes(myString);
                return Convert.ToBase64String(Desdencrypt.TransformFinalBlock(buff, 0, buff.Length)).Replace("+", "Vydduh");
            }

            public static string Decrypt(string myString)
            {
                string Key = "8b,g[viNiuj";
                TripleDESCryptoServiceProvider CryptDES3 = new TripleDESCryptoServiceProvider();
                MD5CryptoServiceProvider CryptMD5Hash = new MD5CryptoServiceProvider();
                CryptDES3.Key = CryptMD5Hash.ComputeHash(ASCIIEncoding.ASCII.GetBytes(Key));
                CryptDES3.Mode = CipherMode.ECB;
                ICryptoTransform Desdencrypt = CryptDES3.CreateDecryptor();
                byte[] buff = Convert.FromBase64String(myString.Replace("Vydduh", "+"));
                return ASCIIEncoding.ASCII.GetString(Desdencrypt.TransformFinalBlock(buff, 0, buff.Length));
            }
        }
    }
}
